package com.agree.util.excel.handlers.mapping;

import com.agree.util.excel.entity.InterfaceColMapEntity;
import com.agree.util.excel.entity.TableInfo;
import com.agree.util.excel.enums.*;
import com.agree.util.excel.handlers.mapping.params.GetParamsHandler;
import com.agree.util.excel.util.StringUtils;
import org.springframework.beans.BeanUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Created by wangmu on 2017/12/1.
 */
public abstract class MappingHandler {

    protected MappingConfig mappingConfig;

    protected Mapper mapper;

    public MappingHandler(MappingConfig mappingConfig) {
        this.mappingConfig = mappingConfig;
    }

    //两个对象的属性映射，一般是数据库实体对象到POI对象
    public abstract List<InterfaceColMapEntity> mapProperties(TableInfo tableInfo) throws IllegalAccessException, InstantiationException, IOException;

    protected InterfaceColMapEntity getReqHeader(TableInfo source) {
        InterfaceColMapEntity reqHeader = new InterfaceColMapEntity();
        if(mapper != null) {
            Entity entity = mapper.reqHeader();
            BeanUtils.copyProperties(entity,reqHeader);
        }
        return reqHeader;
    }

    protected void insertOrderNum(List<InterfaceColMapEntity> result) {
        final int[] i = {0};
        result.forEach(r -> r.setColnum(i[0]++));
    }

    protected List<InterfaceColMapEntity> getReqParams(TableInfo source) {
        //获取入参
        return getParamsByType(source,0);
    }

    private List<InterfaceColMapEntity> getParamsByType(TableInfo source,int type) {
        //获取入参
        Params params = null;
        List<InterfaceColMapEntity> oneEntitys = null;
        if(mapper != null && type ==0) {
            params = mapper.reqParams();
        } else if(mapper != null && type ==1) {
            params = mapper.rspParams();
            List<Entity> entities = params.getAllEntity();
            oneEntitys = entities.stream()
                    .filter(e -> e != null)
                    .map(e -> {
                        InterfaceColMapEntity entity = new InterfaceColMapEntity();
                        BeanUtils.copyProperties(e,entity);
                        return entity;
                        }).collect(Collectors.toList());
        }
        List<String> finalRequestParams = params.getParamsNames();
        Params finalParams = params;
        List<InterfaceColMapEntity> bodys = source.getFields()
                .stream()
                .filter(s -> mapper != null && finalRequestParams.contains(s.getHumpName()))
                .map(s -> {
                    InterfaceColMapEntity reqParam = new InterfaceColMapEntity();
                    Entity everyone = finalParams.everyone();
                    Class<? extends Entity> aClass = everyone.getClass();
                    Field[] declaredFields = aClass.getDeclaredFields();
                    Field.setAccessible(declaredFields, true);
                    for (Field f : declaredFields) {
                        if (f.getName().equals("this$0")) {
                            continue;
                        }
                        try {
                            Object invoke = f.get(everyone);
                            String s1 = String.valueOf(invoke);
                            if (s1.contains("#{") && s1.contains("}")) {
                                String substring = s1.substring(s1.indexOf("#{") + 2, s1.indexOf("}"));
                                Method method = s.getClass().getMethod("get" + StringUtils.firstToUpperCase(substring));
                                Object invoke1 = method.invoke(s);
                                invoke = s1.replace(s1.substring(s1.indexOf("#{"), s1.indexOf("}") + 1), String.valueOf(invoke1));
                            }
                            Field field = reqParam.getClass().getDeclaredField(f.getName());
                            Field.setAccessible(new Field[]{field}, true);
                            field.set(reqParam, invoke);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        } catch (NoSuchMethodException e) {
                            e.printStackTrace();
                        } catch (NoSuchFieldException e) {
                            e.printStackTrace();
                        }

                    }
                    return reqParam;
                }).collect(Collectors.toList());
        if(oneEntitys != null) {
            oneEntitys.addAll(bodys);
            return oneEntitys;
        }
        return bodys;
    }

    protected List<InterfaceColMapEntity> getReqParamsPage(TableInfo source) {
        //设置分页
        InterfaceColMapEntity reqPage = new InterfaceColMapEntity();
        InterfaceColMapEntity reqRows = new InterfaceColMapEntity();
        if(mapper != null) {
            Entity page = mapper.reqPage().getPage();
            Entity count = mapper.reqPage().getCount();
            BeanUtils.copyProperties(page,reqPage);
            BeanUtils.copyProperties(count,reqRows);
        }
        List<InterfaceColMapEntity> pages = new ArrayList<>();
        pages.add(reqPage);
        pages.add(reqRows);
        return pages;
    }

    protected InterfaceColMapEntity getRspHeader(TableInfo source) {
        InterfaceColMapEntity rspHeader = new InterfaceColMapEntity();
        if(mapper != null) {
            Entity entity = mapper.rspHeader();
            BeanUtils.copyProperties(entity,rspHeader);
        }
        return rspHeader;
    }

    protected List<InterfaceColMapEntity> getRspParamsPage(TableInfo source) {
        //设置分页
        InterfaceColMapEntity rspPage = new InterfaceColMapEntity();
        InterfaceColMapEntity rspRows = new InterfaceColMapEntity();

        if(mapper != null) {
            Entity page = mapper.rspPage().getPage();
            Entity count = mapper.rspPage().getCount();
            BeanUtils.copyProperties(page,rspPage);
            BeanUtils.copyProperties(count,rspRows);
        }
        List<InterfaceColMapEntity> pages = new ArrayList<>();
        pages.add(rspPage);
        pages.add(rspRows);
        return pages;
    }

    protected List<InterfaceColMapEntity> getRspParams(TableInfo source) {
        //设置容器体
        return getParamsByType(source,1);
    }

    protected abstract MappingType getType();//子类实现类型

    //配置方法
    protected  void configure(TableInfo source) throws InstantiationException, IllegalAccessException, IOException {
        startMapping()
                .reqHeader().setMsgtypeid(mappingConfig.getTranCode()+"."+ ParamConst.REQ_INFO.getCode()).setMsgtypename(mappingConfig.getComment() == null ? source.getComment()+getType().getValue() : mappingConfig.getComment())
                    .setColname("/"+ ParamConst.REQ_INFO.getName()).setColdesc(ParamConst.REQ_INFO.getDesc()).end()
                .rspHeader().setMsgtypeid(mappingConfig.getTranCode()+"."+ ParamConst.RSP_INFO.getCode()).setMsgtypename(mappingConfig.getComment() == null ? source.getComment()+getType().getValue() : mappingConfig.getComment())
                    .setColname("/"+ParamConst.RSP_INFO.getName()).setColdesc(ParamConst.RSP_INFO.getDesc()).end()
                .reqPage().page().setMsgtypeid(mappingConfig.getTranCode()+"."+ ParamConst.REQ_INFO.getCode()).setMsgtypename(mappingConfig.getComment() == null ? source.getComment()+getType().getValue() : mappingConfig.getComment())
                    .setColname("/"+ ParamConst.REQ_INFO.getName()+"/"+ParamConst.REQ_PAGE.getName()).setColdesc(ParamConst.REQ_PAGE.getDesc()).setColflag(ColumnFlag.F.name()).setColtype(ColumnType.TEXT_STRING.getValue()).setCollength("4")
                    .setColmapname("/"+ParamConst.REQ_PAGE.getCode()).end()
                .reqPage().count().setMsgtypeid(mappingConfig.getTranCode()+"."+ ParamConst.REQ_INFO.getCode()).setMsgtypename(mappingConfig.getComment() == null ? source.getComment()+getType().getValue() : mappingConfig.getComment())
                    .setColname("/"+ ParamConst.REQ_INFO.getName()+"/"+ParamConst.REQ_COUNT.getName()).setColdesc(ParamConst.REQ_COUNT.getDesc()).setColflag(ColumnFlag.F.name()).setColtype(ColumnType.TEXT_STRING.getValue()).setCollength("4")
                    .setColmapname("/"+ParamConst.REQ_COUNT.getCode()).end()
                .rspPage().page().setMsgtypeid(mappingConfig.getTranCode()+"."+ ParamConst.RSP_INFO.getCode()).setMsgtypename(mappingConfig.getComment() == null ? source.getComment()+getType().getValue() : mappingConfig.getComment())
                    .setColname("/"+ParamConst.RSP_INFO.getName()+"/"+ParamConst.RSP_PAGE.getName()).setColdesc(ParamConst.RSP_PAGE.getDesc()).setColflag(ColumnFlag.F.name()).setColtype(ColumnType.TEXT_STRING.getValue()).setCollength("4")
                    .setColmapname("/"+mappingConfig.getMappingCodePrefix()+"_"+mappingConfig.getMappingCode()+"/"+ParamConst.RSP_PAGE.getCode()).end()
                .rspPage().count().setMsgtypeid(mappingConfig.getTranCode()+"."+ ParamConst.RSP_INFO.getCode()).setMsgtypename(mappingConfig.getComment() == null ? source.getComment()+getType().getValue() : mappingConfig.getComment())
                    .setColname("/"+ParamConst.RSP_INFO.getName()+"/"+ParamConst.RSP_COUNT.getName()).setColdesc(ParamConst.RSP_COUNT.getDesc()).setColflag(ColumnFlag.F.name()).setColtype(ColumnType.TEXT_STRING.getValue()).setCollength("4")
                    .setColmapname("/"+mappingConfig.getMappingCodePrefix()+"_"+mappingConfig.getMappingCode()+"/"+ParamConst.RSP_COUNT.getCode()).end()
                .reqParams().anyColumn(GetParamsType.KEY,source).everyone().setMsgtypeid(mappingConfig.getTranCode()+"."+ ParamConst.REQ_INFO.getCode()).setMsgtypename(mappingConfig.getComment() == null ? source.getComment()+getType().getValue() : mappingConfig.getComment())
                    .setColname("/"+ ParamConst.REQ_INFO.getName()+"/" + "#{humpName}").setColdesc("#{comment}").setColflag(ColumnFlag.F.name()).setColtype(ColumnType.TEXT_STRING.getValue()).setCollength("#{maxIntegerLength}")
                    .setColmapname("/#{humpName}").setColmust("#{must}").end()
                .rspParams().oneColumn().setMsgtypeid(mappingConfig.getTranCode()+"."+ ParamConst.RSP_CON.getCode()).setMsgtypename(mappingConfig.getComment() == null ? source.getComment()+getType().getValue() : mappingConfig.getComment())
                        .setColname("/"+ ParamConst.RSP_CON.getName()+"/"+source.getHumpName()).setColdesc(ParamConst.RSP_CON.getDesc()).setColflag(ColumnFlag.OA.name())
                        .setColmapname("/"+mappingConfig.getMappingCodePrefix()+"_"+mappingConfig.getMappingCode()+"/"+source.getHumpName()).end().rspParams()
                    .anyColumn(GetParamsType.ALL,source).everyone().setMsgtypeid(mappingConfig.getTranCode()+"."+ ParamConst.RSP_INFO.getCode()).setMsgtypename(mappingConfig.getComment() == null ? source.getComment()+getType().getValue() : mappingConfig.getComment())
                        .setColname("/"+ParamConst.RSP_INFO.getName()+"/"+ source.getHumpName()  + "/#{humpName}").setColdesc("#{comment}").setColflag(ColumnFlag.A.name()).setColtype(ColumnType.TEXT_STRING.getValue()).setCollength("#{maxIntegerLength}")
                        .setColmapname("/"+mappingConfig.getMappingCodePrefix()+"_" + mappingConfig.getMappingCode() + "/" + source.getHumpName() + "/#{humpName}").setColmust("N").end();
    };

    protected Mapper startMapping() {
        if(this.mapper == null) {
            this.mapper = new Mapper();
        }
        return this.mapper;
    }

    protected class Mapper {

        private Entity reqHeader;
        private Entity rspHeader;
        private Page reqPage;
        private Page rspPage;
        private Params reqParams;
        private Params rspParams;

        public Entity reqHeader() {
            if(this.reqHeader == null) {
                this.reqHeader = new Entity();
            }
            return this.reqHeader;
        }

        public Entity rspHeader() {
            if(this.rspHeader == null) {
                this.rspHeader = new Entity();
            }
            return this.rspHeader;
        }

        public Params reqParams() {
            if(this.reqParams == null) {
                this.reqParams = new Params("req");
            }
            return this.reqParams;
        }

        public Params rspParams() {
            if(this.rspParams == null) {
                this.rspParams = new Params("rsp");
            }
            return this.rspParams;
        }

        public Page reqPage() {
            if(this.reqPage == null) {
                this.reqPage = new Page();
            }
            return this.reqPage;
        }

        public Page rspPage() {
            if(this.rspPage == null) {
                this.rspPage = new Page();
            }
            return this.rspPage;
        }

        public Mapper end() {
            return this;
        }

        @Override
        public String toString() {
            return "Mapper{" +
                    "reqHeader=" + reqHeader +
                    ", rspHeader=" + rspHeader +
                    ", reqPage=" + reqPage +
                    ", rspPage=" + rspPage +
                    ", reqParams=" + reqParams +
                    ", rspParams=" + rspParams +
                    '}';
        }
    }

    protected class Entity {
        private String msgtypeid;

        private String msgtypename;

        private int colnum;

        private String colname;

        private String colflag = "O";

        private String coltype = "Object";

        private String collength = "9999";

        private String coldesc;

        private String colmust = "Y";

        private String colmapname = "/";

        public String getMsgtypeid() {
            return msgtypeid;
        }

        public Entity setMsgtypeid(String msgtypeid) {
            this.msgtypeid = msgtypeid;
            return this;
        }

        public String getMsgtypename() {
            return msgtypename;
        }

        public Entity setMsgtypename(String msgtypename) {
            this.msgtypename = msgtypename;
            return this;
        }

        public int getColnum() {
            return colnum;
        }

        public Entity setColnum(int colnum) {
            this.colnum = colnum;
            return this;
        }

        public String getColname() {
            return colname;
        }

        public Entity setColname(String colname) {
            this.colname = colname;
            return this;
        }

        public String getColflag() {
            return colflag;
        }

        public Entity setColflag(String colflag) {
            this.colflag = colflag;
            return this;
        }

        public String getColtype() {
            return coltype;
        }

        public Entity setColtype(String coltype) {
            this.coltype = coltype;
            return this;
        }

        public String getCollength() {
            return collength;
        }

        public Entity setCollength(String collength) {
            this.collength = collength;
            return this;
        }

        public String getColdesc() {
            return coldesc;
        }

        public Entity setColdesc(String coldesc) {
            this.coldesc = coldesc;
            return this;
        }

        public String getColmust() {
            return colmust;
        }

        public Entity setColmust(String colmust) {
            this.colmust = colmust;
            return this;
        }

        public String getColmapname() {
            return colmapname;
        }

        public Entity setColmapname(String colmapname) {
            this.colmapname = colmapname;
            return this;
        }

        public Mapper end() {
            return MappingHandler.this.mapper;
        }

        @Override
        public String toString() {
            return "Entity{" +
                    "msgtypeid='" + msgtypeid + '\'' +
                    ", msgtypename='" + msgtypename + '\'' +
                    ", colnum=" + colnum +
                    ", colname='" + colname + '\'' +
                    ", colflag='" + colflag + '\'' +
                    ", coltype='" + coltype + '\'' +
                    ", collength='" + collength + '\'' +
                    ", coldesc='" + coldesc + '\'' +
                    ", colmust='" + colmust + '\'' +
                    ", colmapname='" + colmapname + '\'' +
                    '}';
        }
    }

    protected class Page {
        private Entity currentPage;

        private Entity count;

        public Entity page() {
            currentPage = new Entity();
            return currentPage;
        }

        public Entity getPage() {
            return this.currentPage;
        }

        public Entity count() {
            count = new Entity();
            return count;
        }

        public Entity getCount() {
            return this.count;
        }

        @Override
        public String toString() {
            return "Page{" +
                    "currentPage=" + currentPage +
                    ", count=" + count +
                    '}';
        }
    }

    protected class Params {

        private String type;

        private GetParamsType getParamsType;

        private Entity everyone;

        private List<Entity> entities = new ArrayList<>();

        private List<String> paramsName;

        public Params(String type) {
            this.type = type;
        }

        public Params anyColumn(GetParamsType type, TableInfo tableInfo, String... param) throws IllegalAccessException, InstantiationException, IOException {
            GetParamsHandler handler = type.getHandler().newInstance();
            getParamsType = type;
            paramsName = handler.getParams(tableInfo, param);
            return this;
        }

        public Entity everyone() {
            if(everyone == null) {
                this.everyone = new Entity();
            }
            return this.everyone;
        }

        public Entity oneColumn() {
            Entity entity = new Entity();
            entities.add(entity);
            return entity;
        }

        public List<Entity> getAllEntity() {
            return entities;
        }

        public List<String> getParamsNames() {
            return this.paramsName;
        }

        public GetParamsType getGetParamsType() {
            return getParamsType;
        }

        @Override
        public String toString() {
            return "Params{" +
                    "type='" + type + '\'' +
                    ", getParamsType=" + getParamsType +
                    ", everyone=" + everyone +
                    ", entities=" + entities +
                    ", paramsName=" + paramsName +
                    '}';
        }
    }


}
